package com.aracky.assess.service.impl;

import com.aracky.assess.model.entity.HrKpiUserPerformanceHeader;
import com.aracky.assess.model.entity.SysUser;
import com.aracky.assess.model.vo.HrKpiUserPerformanceHeaderVO;
import com.aracky.assess.outside.WxMsg;
import com.aracky.assess.service.HrKpiUserPerformanceHeaderService;
import com.aracky.assess.service.PushMsgService;
import com.aracky.assess.service.SysUserService;
import com.aracky.common.mapper.BaseMapper;
import com.aracky.common.util.ComUtil;
import com.aracky.common.util.ConvertUtil;
import com.aracky.common.util.wxutil.WxMsgUtil;
import com.aracky.common.vo.DictVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

import static com.aracky.assess.constants.ComConstants.KPI.*;


/**
 * @Author zengzhiping
 * @BelongsPackage com.aracky.system.service.impl
 * @CreateTime 2022-07-26
 * @Description 推送企业微信消息
 */
@Service
public class PushMsgServiceImpl implements PushMsgService {

    private static Logger logger = LoggerFactory.getLogger(PushMsgServiceImpl.class);

    @Value("${wx.config.wxLoginUrl}")
    private String wxLoginUrl;

    @Autowired
    private WxMsgUtil wxMsgUtil;

    @Autowired
    private HrKpiUserPerformanceHeaderService hrKpiUserPerformanceHeaderService;


    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private Executor asyncTaskExecutor;

    @Autowired
    private RedisTemplate redisTemplate;


    @Resource
    private BaseMapper baseMapper;

    /**
     * 消息格式
     * 您当月应评人数有10人
     * 已填完评价人数有6人
     * 未完成评价人数有4人
     */
    @SneakyThrows
    @Override
    public void pushScoreMsgToAssessor() {

        //查询任务信息-结束填报日期前2天开始通知评分人
        LocalDate currentDate = LocalDate.now();

        // 条件构造（获取离最后填报时间差两天的header信息）
        LambdaQueryWrapper<HrKpiUserPerformanceHeader> headerLambdaQueryWrapper =
                Wrappers.<HrKpiUserPerformanceHeader>lambdaQuery()
                        .notIn(HrKpiUserPerformanceHeader::getStatus, HR_COMPLETE_STRINGS, END_COMPLETE_STRINGS)
                        .le(HrKpiUserPerformanceHeader::getEndWriteDate, currentDate.plusDays(2L))
                        .and(temp -> {
                            temp.eq(HrKpiUserPerformanceHeader::getAssessor1Status, Boolean.FALSE)
                                    .or()
                                    .eq(HrKpiUserPerformanceHeader::getAssessor2Status, Boolean.FALSE);
                        });
        List<HrKpiUserPerformanceHeader> headList = hrKpiUserPerformanceHeaderService.list(headerLambdaQueryWrapper);

        List<HrKpiUserPerformanceHeaderVO> headerList = this.getKpiUserList(headList);
        if (ComUtil.isNullList(headerList)) {
            return;
        }

        //查询用户信息
        Set<Long> userIdSet = new HashSet(headerList.size());
        headerList.forEach(head -> {
            if (!head.getAssessor1Status()) {
                userIdSet.add(head.getAssessor1());
            }
            if (!head.getAssessor2Status()) {
                userIdSet.add(head.getAssessor2());
            }
        });
        //获取用户信息
        Map<Long, String> userMap = getUserMap(userIdSet);
        if (userMap.isEmpty()) {
            return;
        }
        //开始发送消息
        SendAssessorMsg(headerList, userMap);

    }

    /**
     * 获取多个数据
     *
     * @param headerList
     * @return
     */
    private List<HrKpiUserPerformanceHeaderVO> getKpiUserList(List<HrKpiUserPerformanceHeader> headerList) {
        List<HrKpiUserPerformanceHeaderVO> headerVOList =
                ConvertUtil.sourceToTargets(headerList, HrKpiUserPerformanceHeaderVO.class);
        if (ComUtil.isNullList(headerVOList)) {
            return headerVOList;
        }

        Map<String, String> dictMap = this.getDictMap();
        headerVOList.stream().forEach(header -> {
            String name = dictMap.get(header.getPerformanceType());
            header.setPerformanceTypeName(name);
        });
        return headerVOList;
    }

    //获取单个数据
    private HrKpiUserPerformanceHeaderVO getKpiUser(HrKpiUserPerformanceHeader header) {
        HrKpiUserPerformanceHeaderVO headerVO = ConvertUtil.sourceToTarget(header, HrKpiUserPerformanceHeaderVO.class);
        Map<String, String> dictMap = this.getDictMap();
        String name = dictMap.get(header.getPerformanceType());
        headerVO.setPerformanceTypeName(name);
        return headerVO;
    }

    /**
     * 获取字典内容
     *
     * @return
     */
    private Map<String, String> getDictMap() {
        Map<String, String> dictMap = null;
        List<String> dictCodeList = new ArrayList<>();
        dictCodeList.add("PERFORMANCE_TYPE");

        List<DictVO> dictList = baseMapper.findDictList(dictCodeList);
        Map<String, List<DictVO>> resultMap = dictList.stream().collect(Collectors.groupingBy(DictVO::getCode));

        List<DictVO> dictVOList = resultMap.get("PERFORMANCE_TYPE");
        if (ComUtil.isNullList(dictVOList)) {
            return dictMap;
        }
        dictMap = dictVOList.stream().collect(Collectors.toMap(DictVO::getValue, DictVO::getName));
        return dictMap;
    }


    /**
     * 获取用户信息
     *
     * @param userIdSet
     * @return
     */
    private Map<Long, String> getUserMap(Set<Long> userIdSet) {
        List<SysUser> sysUsers = sysUserService.listByIds(userIdSet);
        //用户id和微信用户id
        return sysUsers.stream().filter(sysUser -> StringUtils.isNotBlank(sysUser.getWxUserId()))
                .collect(Collectors.toMap(SysUser::getUserId, SysUser::getWxUserId));
    }

    /**
     * 给评分人一发送消息
     *
     * @param headerList
     * @param userMap
     * @throws UnsupportedEncodingException
     */
    private void SendAssessorMsg(List<HrKpiUserPerformanceHeaderVO> headerList, Map<Long, String> userMap) {

        //那些消息需要发送：评分人一样，考核类型一样，考核周期一样，归为一种消息
        //需要根据评分人一分组
        CompletableFuture<Void> oneCompletableFuture = CompletableFuture.runAsync(() -> {
            Map<String, List<HrKpiUserPerformanceHeaderVO>> assessorOneList =
                    headerList.stream().filter(head -> !head.getAssessor1Status()).collect(Collectors.groupingBy(
                            head -> head.getAssessor1() + head.getPerformanceType() + head.getPerformancePeriod()));
            SendAssessorWxMsg(userMap, assessorOneList, ASSESSOR1);
        }, asyncTaskExecutor);

        //第二评分人
        CompletableFuture<Void> twoCompletableFuture = CompletableFuture.runAsync(() -> {
            Map<String, List<HrKpiUserPerformanceHeaderVO>> assessorTwoList =
                    headerList.stream().filter(head -> !head.getAssessor2Status()).collect(Collectors.groupingBy(
                            head -> head.getAssessor2() + head.getPerformanceType() + head.getPerformancePeriod()));
            SendAssessorWxMsg(userMap, assessorTwoList, ASSESSOR2);
        }, asyncTaskExecutor);
        //等待调用完成
        CompletableFuture.allOf(oneCompletableFuture, twoCompletableFuture).join();
    }

    /**
     * 给执行人发送消息
     *
     * @param userMap
     * @param assessorList
     * @throws UnsupportedEncodingException
     */
    private void SendAssessorWxMsg(Map<Long, String> userMap, Map<String, List<HrKpiUserPerformanceHeaderVO>> assessorList, String assess) {
        List<String> taskIdList;
        String touser;
        String title;
        String descriptions;
        String url;
        String[] replaceList;
        //构造发送消息
        for (Map.Entry<String, List<HrKpiUserPerformanceHeaderVO>> entry : assessorList.entrySet()) {
            List<HrKpiUserPerformanceHeaderVO> headList = entry.getValue();
            taskIdList = new ArrayList<>(headList.size());

            //计算人数
            replaceList = this.calculationCount(headList, taskIdList);

            HrKpiUserPerformanceHeaderVO head = headList.get(0);

            Long assessor = StringUtils.equals(ASSESSOR1, assess) ? head.getAssessor1() : head.getAssessor2();

            touser = userMap.get(assessor);
            title = head.getPerformancePeriod() + "员工" + head.getPerformanceTypeName() + "评分通知";

            descriptions = StringUtils.replaceEach(ASSESSOR_MSG_TEMPLATE, ASSESSOR_REPLACE_LIST, replaceList);
            url = wxLoginUrl + "?wxUserId=" + touser + "&path=" + LIST_VIEW + "&taskIds=" + String.join(",", taskIdList);
            try {
                wxMsgUtil.sendCardMessage(touser, title, descriptions, url);
            } catch (Exception e) {
                logger.error("err info:{}", e.getMessage());
                //返回错误信息，作为补偿消息定时发送
            }

        }
    }

    /**
     * 计算人数
     *
     * @param headList
     * @param taskIdList
     * @return
     */
    private String[] calculationCount(List<HrKpiUserPerformanceHeaderVO> headList, List<String> taskIdList) {
        //统计当月应评价人数,已填写评分人数，未评分人数
        int allTotal = 0;
        int completeTotal = 0;
        int noCompleteTotal = 0;
        for (HrKpiUserPerformanceHeaderVO head : headList) {
            allTotal++;
            if (ASSESSOR_COMPLETE_STRINGS.equals(head.getStatus())) {
                completeTotal++;
                continue;
            }
            noCompleteTotal++;
            taskIdList.add(String.valueOf(head.getId()));
        }
        return new String[]{String.valueOf(allTotal), String.valueOf(completeTotal), String.valueOf(noCompleteTotal)};
    }


    @Override
    public void pushWriteMsgToEmployee(List<HrKpiUserPerformanceHeader> headList) {
        logger.info("start send msg to wxchat.....");
        if (ComUtil.isNullList(headList)) {
            //查询所有未填写的数据
            LambdaQueryWrapper<HrKpiUserPerformanceHeader> headerLambdaQueryWrapper =
                    Wrappers.<HrKpiUserPerformanceHeader>lambdaQuery()
                            .ge(HrKpiUserPerformanceHeader::getEndWriteDate, LocalDate.now())
                            .last(" and ( status IS NULL or status = '')");
            headList = hrKpiUserPerformanceHeaderService.list(headerLambdaQueryWrapper);
        }
        //通过任务id查询数据
        List<HrKpiUserPerformanceHeaderVO> headerList = getKpiUserList(headList);

        if (ComUtil.isNullList(headerList)) {
            return;
        }

        //查询用户信息
        Set<Long> userIdSet = headerList.stream().map(HrKpiUserPerformanceHeaderVO::getUserId).collect(Collectors.toSet());

        //获取用户信息
        Map<Long, String> userMap = getUserMap(userIdSet);
        if (userMap.isEmpty()) {
            return;
        }

        //存放返回结果：
        List<WxMsg> errMsgList = new ArrayList<>();

        String touser;
        String title;
        String descriptions;
        String url;
        String[] replaceList;
        //构造发送消息
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
        for (HrKpiUserPerformanceHeaderVO head : headerList) {

            touser = userMap.get(head.getUserId());
            title = "👇【" + head.getPerformancePeriod() + "员工" + head.getPerformanceTypeName() + "填写通知】";

            replaceList = new String[]{head.getEndWriteDate().format(formatter),
                    head.getPerformancePeriod() + head.getPerformanceTypeName(),
                    head.getEndAssessorDate().format(formatter)};

            descriptions = StringUtils.replaceEach(EMP_MSG_TEMPLATE, EMP_REPLACE_LIST, replaceList);
            url = wxLoginUrl + "?wxUserId=" + touser + "&path=" + EMP_VIEW + "&taskId=" + head.getId();
            try {
                wxMsgUtil.sendCardMessage(touser, title, descriptions, url);
            } catch (Exception e) {
                logger.error("err info:{}", e.getMessage());
                //返回错误信息，作为补偿消息定时发送
                errMsgList.add(new WxMsg(touser, title, descriptions, url));
            }
        }

        //失败的消息发送存入redis，通过定时任务再次发送
        if (!errMsgList.isEmpty()) {
            LocalDate currentDate = LocalDate.now();
            //公共key+年月
            String key = WRITE_REDIS_MSG_KEY + currentDate.getYear() + currentDate.getMonthValue();
            redisTemplate.opsForList().leftPushAll(key, errMsgList);
        }

    }


    @Override
    public void pushCompleteMsgToEmployee(Long taskId, Long userId) {
        //查询微信id
        SysUser sysUser = sysUserService.getById(userId);
        String touser = sysUser.getWxUserId();
        //查询具体消息
        HrKpiUserPerformanceHeader dbHeader = hrKpiUserPerformanceHeaderService.getById(taskId);

        //通过任务id查询数据
        HrKpiUserPerformanceHeaderVO header = this.getKpiUser(dbHeader);

        String title = "✨【" + header.getPerformancePeriod() + header.getPerformanceTypeName() + "分数查看提醒】";
        String descriptions = "您" + header.getPerformancePeriod() + header.getPerformanceTypeName() + "分数已评估完成，请进入详情查看";
        String url = wxLoginUrl + "?wxUserId=" + touser + "&path=" + EMP_VIEW + "&taskId=" + header.getId();
        try {
            wxMsgUtil.sendCardMessage(touser, title, descriptions, url);
        } catch (Exception e) {
            logger.error("err info:{}", e.getMessage());
            LocalDate currentDate = LocalDate.now();
            //公共key+年月
            String key = COMPLETE_REDIS_MSG_KEY + currentDate.getYear() + currentDate.getMonthValue();
            //返回错误信息，作为补偿消息定时发送
            WxMsg wxMsg = new WxMsg(touser, title, descriptions, url);
            redisTemplate.opsForList().leftPush(key, wxMsg);
        }

    }

    @Override
    public void pushRedisMsg() {
        //取出redis中的消息
        LocalDate currentDate = LocalDate.now();

        //发送评分完成失败的消息
        String key = COMPLETE_REDIS_MSG_KEY + currentDate.getYear() + currentDate.getMonthValue();
        this.sendRedisMsg(key);

        //发送员工填写发送失败的消息
        key = WRITE_REDIS_MSG_KEY + currentDate.getYear() + currentDate.getMonthValue();
        this.sendRedisMsg(key);
    }

    /**
     * 发送redis中的消息
     *
     * @param key
     */
    private void sendRedisMsg(String key) {
        if (redisTemplate.hasKey(key)) {
            List<WxMsg> msgList = redisTemplate.opsForList().range(key, 0, -1);
            //遍历数据，进行发送
            for (WxMsg msg : msgList) {
                try {
                    wxMsgUtil.sendCardMessage(msg.getTouser(), msg.getTitle(), msg.getDescriptions(), msg.getUrl());
                    redisTemplate.opsForList().remove(key, 0, msg);
                } catch (Exception e) {
                    logger.error("err info:{}", e.getMessage());
                }
            }
            //判断长度，直接删除key
            Long size = redisTemplate.opsForList().size(key);
            if (size > 0) {
                return;
            }
            redisTemplate.delete(key);
        }
    }
}
